package com.hanwei.uap.acceptors.sstn.service;

import com.hanwei.uap.acceptors.api.channel.ChannelManager;
import com.hanwei.uap.acceptors.api.constants.Server;
import com.hanwei.uap.acceptors.api.service.CommandService;
import com.hanwei.uap.common.constant.Common;
import com.hanwei.uap.common.util.JsonUtil;
import com.hanwei.uap.model.po.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.BoundListOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

@Service
public class CommandServiceImpl implements CommandService {
    private Logger log = LoggerFactory.getLogger("command");
    public static Map<String,Command> deviceCommandMap = new ConcurrentHashMap<String,Command>();
    @Autowired
    private Environment env;
    private String serverHost = Server.DEFAULT_BIND_HOST;
    private int serverPort = Server.DEFAULT_BIND_PORT;
    private String commandSynQname = Server.DEFAULT_Q_COMMAND_SYN;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private ChannelManager channelManager;

    @PostConstruct
    private void init() {
        if (env.getProperty("uap.server.host") != null) {
            serverHost = env.getProperty("uap.server.host");
        }
        if (env.getProperty("uap.server.port") != null) {
            serverPort = env.getProperty("uap.server.port", Integer.class);
        }
        if (env.getProperty("uap.qname.commandsyn") != null) {
            commandSynQname = env.getProperty("uap.qname.commandsyn");
        }
        commandSynQname = commandSynQname + "_" + serverHost + "_" + serverPort;
        pullCommand();
        log.info("init over,commandSynQname:{}", commandSynQname);
    }

    @Override
    public void pullCommand() {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.execute(new Runnable() {
            @Override
            public void run() {
                BoundListOperations<String, String> list = stringRedisTemplate.boundListOps(commandSynQname);
                while (true) {
                    String command = list.leftPop(60, TimeUnit.SECONDS);
                    if (log.isInfoEnabled())
                        log.info("pop command is:{}",command);
                    if (null != command) {
                        try {
                            execute(command);
                        } catch (Exception e) {
                            log.warn(command +", command execute exception!",e);
                        }
                    }
                }
            }
        });
    }

    @Override
    public void execute(String command) {
        Command cmd = JsonUtil.toBean(command, Command.class);
        execute(cmd);
    }

    @Override
    public void execute(Command cmd) {
        if (Common.CMD_FLAG_SYN.equals(cmd.getFlag())) {
            try {
                boolean success = channelManager.sendCommand(cmd);
                if(!success){
                    cmd.setPuttime(System.currentTimeMillis());
                    if(cmd.getTimeout()==0){
                        cmd.setTimeout(Common.CMD_TIMEOUT);
                    }
                    deviceCommandMap.put(cmd.getDeviceId(), cmd);
                }
            } catch (Exception e) {
                log.warn("Send Syn Command To Device[" + cmd.getDeviceId() + "] Exception!", e);
            }
        }
    }

    @Override
    public Command getCommand(String deviceId) {
        Command command = deviceCommandMap.remove(deviceId);
        if (command == null || (System.currentTimeMillis() - command.getPuttime()) / 1000 > command.getTimeout())
            return null;
        return command;
    }
}
